package wechat

import (
	"context"
	"encoding/base64"
	binaryUtils "encoding/binary"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"time"

	"code.gitea.io/gitea/modules/log"
	"code.gitea.io/gitea/modules/setting"
	context2 "code.gitea.io/gitea/services/context"
	"github.com/google/uuid"
)

// ResultType 定义了响应格式
type ResultType struct {
	Code int         `json:"code"`
	Msg  string      `json:"msg"`
	Data interface{} `json:"data,omitempty"` // Data 字段可选
}

// RespondJson2HttpResponseWriter
// 将 ResultType 响应转换为 JSON 并写入响应
func (t *ResultType) RespondJson2HttpResponseWriter(w http.ResponseWriter) {
	responseBytes, err := json.Marshal(*t)
	if err != nil {
		// 只有序列化 ResultType 失败时候, 才返回 HTTP 500 Internal Server Error
		http.Error(w, http.StatusText(http.StatusInternalServerError)+": failed to marshal JSON", http.StatusInternalServerError)
		return
	}

	// 序列化 ResultType 成功，无论成功或者失败，统一返回 HTTP 200 OK
	if setting.CORSConfig.Enabled {
		AllowOrigin := setting.CORSConfig.AllowDomain[0]
		if AllowOrigin == "" {
			AllowOrigin = "*"
		}
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.Header().Set("Access-Control-Allow-Origin", AllowOrigin)
		w.Header().Set("Access-Control-Allow-Methods", strings.Join(setting.CORSConfig.Methods, ","))
		w.Header().Set("Access-Control-Allow-Headers", strings.Join(setting.CORSConfig.Headers, ","))
	} else {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.Header().Set("Access-Control-Allow-Origin", "*")
		w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
		w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
	}
	w.WriteHeader(http.StatusOK)
	_, _ = w.Write(responseBytes)
}

// RespSuccess 操作成功常量
var RespSuccess = ResultType{
	Code: 0,
	Msg:  "操作成功",
}

// RespUnauthorizedFailure 获取 devContainer 信息失败：用户未授权
var RespUnauthorizedFailure = ResultType{
	Code: 00001,
	Msg:  "未登录，禁止访问",
}

// 错误码 100xx 表示微信相关错误信息

// RespFailedWechatMalconfigured 微信配置错误，功能不可用
var RespFailedWechatMalconfigured = ResultType{
	Code: 10000,
	Msg:  "微信配置错误，功能不可用",
	//Data: map[string]string{
	//  "ErrorMsg":
	//},
}

// RespPendingQrNotScanned 用户未扫码返回结果常量
var RespPendingQrNotScanned = ResultType{
	Code: 10001,
	Msg:  "用户未扫描微信公众号带参数二维码",
}

// RespFailedIllegalWechatQrTicket 二维码凭证Ticket参数无效
var RespFailedIllegalWechatQrTicket = ResultType{
	Code: 10002,
	Msg:  "提交的微信公众号带参数二维码凭证Ticket参数无效",
}

// RespFailedGenerateWechatOfficialAccountTempQR 微信公众号临时二维码生成失败
var RespFailedGenerateWechatOfficialAccountTempQR = ResultType{
	Code: 10003,
	Msg:  "微信公众号临时二维码生成失败",
	//Data: map[string]string{
	//  "ErrorMsg": err.Error(),
	//},
}

// RespFailedGetWechatOfficialAccountTempQRStatus 获取微信公众号临时二维码扫码状态出错
var RespFailedGetWechatOfficialAccountTempQRStatus = ResultType{
	Code: 10004,
	Msg:  "获取微信公众号临时二维码扫码状态出错",
	//Data: map[string]string{
	//  "ErrorMsg": err.Error(),
	//},
}

// ErrorWechatTempQRStatus 获取微信公众号临时二维码出错
type ErrorWechatTempQRStatus struct {
	Action  string
	Message string
}

func (err ErrorWechatTempQRStatus) Error() string {
	return fmt.Sprintf("Failed to %s in Wechat Service: %s",
		err.Action, err.Message,
	)
}

// WechatTempQRStatus 获取微信公众号临时二维码扫码状态
type WechatTempQRStatus struct {
	IsScanned bool `json:"is_scanned"` // 微信公众号二维码是否已被扫描

	// 下列3个参数如果为空，则在 JSON 中不出现

	SceneStr string `json:"scene_str,omitempty"` // 微信公众号二维码场景值
	OpenId   string `json:"openid,omitempty"`    // 微信公众号二维码扫码人 OpenID
	IsBinded bool   `json:"is_binded,omitempty"` // 微信公众号二维码扫码人 OpenID 是否绑定到了 DevStar UserID
}

// Marshal2JSONString 将结构体解析为 JSON字符串
func (qrStatus WechatTempQRStatus) Marshal2JSONString() (string, error) {

	voJSONBytes, err := json.Marshal(qrStatus)
	if err != nil {
		return "", err
	}
	return string(voJSONBytes), nil
}

// WechatTempQRData 封装微信公众号临时带参数二维码返回值
type WechatTempQRData struct {
	Ticket        string `json:"ticket"`
	ExpireSeconds int64  `json:"expire_seconds"`

	// Url 是指微信端扫码跳转的URL
	Url string `json:"url"`

	// QrImageSrcUrl 是指网页端显示微信二维码图片二维码地址，也即 HTML 中 <img src=`${QrImageUrl}` ... >
	QrImageSrcUrl string `json:"qr_image_url"`
}

// GenerateTempQR 生成微信公众号临时二维码
func GenerateTempQR(
	ctx context.Context,
	sceneStr string, qrExpireSeconds int,
) (*WechatTempQRData, error) {

	// 1. 检查参数 qrExpireSecondsOverride 和 sceneStrOverride: 若用户未指定，则从配置文件 app.ini 读取
	if qrExpireSeconds <= 0 {
		qrExpireSeconds = setting.Wechat.TempQrExpireSeconds
	}
	if len(sceneStr) == 0 {
		// 生成随机 sceneStr 场景值
		//  sceneStr生成规则：UUIDv4后边拼接 当前UnixNano时间戳转为byte数组后的Base64
		//  e.g, sceneStr == "1c78e8d914fb4307a3588ac0f6bc092a@yPXAm+ve5hc="
		bytesArrayUnit64 := make([]byte, 8)
		binaryUtils.LittleEndian.PutUint64(bytesArrayUnit64, uint64(time.Now().UnixNano()))
		currentTimestampNanoBase64 := base64.StdEncoding.EncodeToString(bytesArrayUnit64)
		sceneStr = strings.ReplaceAll(uuid.New().String(), "-", "") + "@" + currentTimestampNanoBase64
	}

	// 2. 调用 Wechat.SDK 生成微信公众号临时二维码
	qrData, err := setting.Wechat.SDK.QRCode.Temporary(ctx, sceneStr, qrExpireSeconds)
	if err != nil {
		return nil, err
	}

	// 3. 封装 VO 返回
	wechatQr := &WechatTempQRData{
		Ticket:        qrData.Ticket,
		ExpireSeconds: qrData.ExpireSeconds,
		Url:           qrData.Url,
		QrImageSrcUrl: "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + qrData.Ticket,
	}
	return wechatQr, nil
}

// GenerateWechatQrCode 生成微信公众号临时带参数二维码
//
//	GET /api/wechat/login/qr/generate?qrExpireSeconds=${qrExpireSeconds}&sceneStr=${sceneStr}
func GenerateWechatQrCode(ctx *context2.APIContext) {

	// 1. 检查微信功能是否启用
	if setting.Wechat.SDK == nil {
		errorMsg := "微信公众号功能禁用, 不会生成公众号带参数二维码"
		log.Warn(errorMsg)
		respFailed := ResultType{
			Code: RespFailedWechatMalconfigured.Code,
			Msg:  RespFailedWechatMalconfigured.Msg,
			Data: map[string]string{
				"ErrorMsg": errorMsg,
			},
		}
		respFailed.RespondJson2HttpResponseWriter(ctx.Resp)
		return
	}

	// 2. 解析 HTTP GET 请求参数，调用 service 层生成二维码
	qrExpireSeconds := ctx.FormInt("qrExpireSeconds")
	sceneStr := ctx.FormString("sceneStr")
	log.Info("sceneStr:" + sceneStr)
	qrCode, err := GenerateTempQR(ctx, sceneStr, qrExpireSeconds)
	if err != nil {
		respFailed := ResultType{
			Code: RespFailedGenerateWechatOfficialAccountTempQR.Code,
			Msg:  RespFailedGenerateWechatOfficialAccountTempQR.Msg,
			Data: map[string]string{
				"ErrorMsg": err.Error(),
			},
		}
		respFailed.RespondJson2HttpResponseWriter(ctx.Resp)
		return
	}

	// 3. 返回 (自动对VO对象进行JSON序列化)
	repsSuccessGenerateQRCode := ResultType{
		Code: RespSuccess.Code,
		Msg:  RespSuccess.Msg,
		Data: qrCode,
	}
	repsSuccessGenerateQRCode.RespondJson2HttpResponseWriter(ctx.Resp)
	return
}

// QrCheckCodeStatus 检查二维码扫描状态
/**
  - 微信服务器验证消息
  - GET /api/wechat/login/qr/check-status
  - 请求参数：
  - ticket: 微信公众号带参数临时二维码 ticket
  - _:      UNIX时间戳，仅用作防止GET请求被缓存，保证每次GET 请求都能够到达服务器
  - 响应参数：（请使用VAR定义）
  - {
    Code: ,  // 状态码，只有在 HTTP 200 OK 后，该字段才有意义
    Msg: ,   //
    Data:
    }
*/
func QrCheckCodeStatus(responseWriter http.ResponseWriter, request *http.Request) {

	// 设置响应头为 JSON 格式
	responseWriter.Header().Set("Content-Type", "application/json")

	// 从请求中提取 ticket 参数
	ticket := request.URL.Query().Get("ticket")
	if ticket == "" {
		RespFailedIllegalWechatQrTicket.RespondJson2HttpResponseWriter(responseWriter)
		return
	}

	qrStatus, err := GetWechatQrStatusByTicket(ticket)
	if err != nil {
		respFailed := ResultType{
			Code: RespFailedGetWechatOfficialAccountTempQRStatus.Code,
			Msg:  RespFailedGetWechatOfficialAccountTempQRStatus.Msg,
			Data: map[string]string{
				"ErrorMsg": err.Error(),
			},
		}
		respFailed.RespondJson2HttpResponseWriter(responseWriter)
		return
	}

	// 将扫码信息返回
	result := ResultType{
		Code: RespSuccess.Code,
		Msg:  RespSuccess.Msg,
		Data: qrStatus,
	}
	result.RespondJson2HttpResponseWriter(responseWriter)
}
